package com.lwh.ssl;

import lombok.extern.slf4j.Slf4j;

import javax.net.ssl.KeyManagerFactory;
import javax.net.ssl.SSLContext;
import javax.net.ssl.TrustManagerFactory;
import javax.net.ssl.X509TrustManager;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.security.*;
import java.security.cert.Certificate;
import java.security.cert.*;
import java.util.ArrayList;
import java.util.Date;

/**
 * @Description:
 * @Date: 2019-09-04
 * @author: luowuhui
 */
@Slf4j
public class SSLUtil {
	public static boolean verify (X509Certificate X509certificateRoot,X509Certificate[] arX509certificate) {

		//声明list，存储证书链中证书主体信息
		ArrayList list = new ArrayList();
		//沿证书链自上而下，验证证书的所有者是下一个证书的颁布者
		Principal principalLast = null;
		int       nSize         = arX509certificate.length;
		for (int i = 0; i < nSize; i++) {//遍历arX509certificate
			X509Certificate x509Certificate = arX509certificate[i];
			//获取发布者标识
			Principal principalIssuer = x509Certificate.getIssuerDN();
			//获取证书的主体标识
			Principal principalSubject = x509Certificate.getSubjectDN();
			//保存证书的序列号
			list.add(x509Certificate.getSerialNumber());

			if (principalLast != null) {
				//验证证书的颁布者是上一个证书的所有者
				if (principalIssuer.equals(principalLast)) {
					try {
						//获取上个证书的公钥
						PublicKey publickey = arX509certificate[i - 1].getPublicKey();
						//验证是否已使用与指定公钥相应的私钥签署了此证书
						arX509certificate[i].verify(publickey);
					} catch (Exception e) {
						return false;
					}
				} else {
					return false;
				}
			}
			principalLast = principalSubject;
		}

		//证明证书链中的第一个证书由用户所信任的CA颁布
		try {
			PublicKey publickey = X509certificateRoot.getPublicKey();
			arX509certificate[0].verify(publickey);
		} catch (Exception e) {
			return false;
		}
		//证明证书链中的最后一个证书的所有者正是现在通信对象
		Principal principalSubject = arX509certificate[nSize - 1].getSubjectDN();
		//验证证书链里每个证书是否在有效期里
		Date date = new Date();
		for (int i = 0; i < nSize; i++) {
			try {
				arX509certificate[i].checkValidity(date);
			} catch (Exception e) {
				return false;
			}
		}
		return true;
	}

	public static boolean verifySign (X509Certificate X509certificateCA,String sign,String original) {
		try {
			//获得签名实例
			Signature signature = Signature.getInstance(X509certificateCA.getSigAlgName());
			//用证书公钥进行初始化
			signature.initVerify(X509certificateCA.getPublicKey());
			//更新源数据
			signature.update(original.getBytes());
			//验证数字签名
			return signature.verify(sign.getBytes());
		} catch (Exception e) {
			return false;
		}
	}

	/**
	 * 校验时间
	 *
	 * @param certificate
	 * @param date
	 * @return
	 */
	public static boolean timeValidity (X509Certificate certificate,Date date) {
		try {
			certificate.checkValidity(date);
			return true;
		} catch (CertificateExpiredException e) {   // 过期
			System.out.println(" Expired ");
			e.printStackTrace();
		} catch (CertificateNotYetValidException e) {//未起效
			e.printStackTrace();
		}
		return false;
	}


	/**
	 * 获取X509 证书
	 *
	 * @param file
	 * @return
	 */
	public static X509Certificate getX509Certificate (File file) {

		if (file != null && file.exists()) {
			try {
				FileInputStream    fileInputStream = new FileInputStream(file);
				CertificateFactory cf              = CertificateFactory.getInstance("X.509");
				Certificate        certificate     = cf.generateCertificate(fileInputStream);
			} catch (FileNotFoundException | CertificateException e) {
				log.error("",e);
			}
		}
		throw new IllegalArgumentException("证书文件读取失败!");
	}

	/**
	 * 根据crt 创建ssl
	 * @param file
	 * @return
	 */
	public static SSLContext getSslContext (File file) {
		SSLContext    sslContext      = null;

		if (sslContext == null) {
			try {
				CertificateFactory cf      = CertificateFactory.getInstance("X.509");
				InputStream        caInput = new FileInputStream(file);
				final Certificate  ca;
				try {
					ca = cf.generateCertificate(caInput);
				} finally {
					caInput.close();
				}

				sslContext = SSLContext.getInstance("TLS");
				sslContext.init(null,new X509TrustManager[]{new X509TrustManager() {
					boolean isServerTrusted = false;
					@Override
					public void checkClientTrusted (X509Certificate[] chain,String authType) throws CertificateException {
						log.info("HttpClientSslHelper","checkClientTrusted --> authType = " + authType);
						//校验客户端证书
					}

					@Override
					public void checkServerTrusted (X509Certificate[] chain,String authType) throws CertificateException {
						log.info("HttpClientSslHelper","checkServerTrusted --> authType = " + authType);
						//校验服务器证书
						for (X509Certificate cert : chain) {
							cert.checkValidity();
							try {
								cert.verify(ca.getPublicKey());
								isServerTrusted = true;
							} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException | SignatureException e) {
								e.printStackTrace();
								isServerTrusted = false;
							}
						}
					}

					@Override
					public X509Certificate[] getAcceptedIssuers () {
						return new X509Certificate[0];
					}
				}},null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sslContext;
	}

	/**
	 * 根据key,ca.crt 创建ssl
	 * @param keyfile
	 * @param pass
	 * @param trustfile
	 * @return
	 */
	public static SSLContext getSslContext (File keyfile,String  pass,File trustfile) {
		SSLContext    sslContext      = null;

		if (sslContext == null) {
			try {
				CertificateFactory cf      = CertificateFactory.getInstance("X.509");
				InputStream        caInput = new FileInputStream(trustfile);
				final Certificate  ca;
				try {
					ca = cf.generateCertificate(caInput);
				} finally {
					caInput.close();
				}
				// 服务器端需要验证的客户端证书
				KeyStore keyStore = KeyStore.getInstance("jks");

				InputStream ksIn =  new FileInputStream(keyfile);
				try {
					keyStore.load(ksIn, pass.toCharArray());
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					try {
						ksIn.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509","SunJSSE");
				keyManagerFactory.init(keyStore, pass.toCharArray());
				sslContext = SSLContext.getInstance("TLS");
				sslContext.init(keyManagerFactory.getKeyManagers(),new X509TrustManager[]{new X509TrustManager() {
					boolean isServerTrusted = false;
					@Override
					public void checkClientTrusted (X509Certificate[] chain,String authType) throws CertificateException {
						log.info("HttpClientSslHelper","checkClientTrusted --> authType = " + authType);
						//校验客户端证书
					}

					@Override
					public void checkServerTrusted (X509Certificate[] chain,String authType) throws CertificateException {
						log.info("HttpClientSslHelper,checkServerTrusted --> authType :{}", authType);
						//校验服务器证书
						for (X509Certificate cert : chain) {
							cert.checkValidity();
							try {
								cert.verify(ca.getPublicKey());
								isServerTrusted = true;
							} catch (NoSuchAlgorithmException | InvalidKeyException | NoSuchProviderException | SignatureException e) {
								e.printStackTrace();
								isServerTrusted = false;
							}
						}
					}

					@Override
					public X509Certificate[] getAcceptedIssuers () {
						return new X509Certificate[0];
					}
				}},null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sslContext;
	}

	/**
	 * 根据p12创建SSLContext
	 * @param p12File
	 * @param pass
	 * @return
	 */
	public static SSLContext getSslContextByP12(File  p12File,String  pass) {
		SSLContext    sslContext      = null;
		if (sslContext == null) {
			try {
				// 服务器端需要验证的客户端证书
				KeyStore keyStore = KeyStore.getInstance("jks");

				InputStream ksIn =  new FileInputStream(p12File);
				try {
					keyStore.load(ksIn, pass.toCharArray());
				} catch (Exception e) {
					e.printStackTrace();
				} finally {
					try {
						ksIn.close();
					} catch (Exception e) {
						e.printStackTrace();
					}
				}
				sslContext = SSLContext.getInstance("TLS");
				// Create a TrustManager that trusts the CAs in our KeyStore
				TrustManagerFactory tmf = TrustManagerFactory.getInstance("X.509");
				tmf.init(keyStore);
				KeyManagerFactory keyManagerFactory = KeyManagerFactory.getInstance("SunX509","SunJSSE");
				keyManagerFactory.init(keyStore, pass.toCharArray());
				sslContext.init(keyManagerFactory.getKeyManagers(), tmf.getTrustManagers(), null);
			} catch (Exception e) {
				e.printStackTrace();
			}
		}
		return sslContext;
	}
}
